#include "ZListView.h"
#include "ZScrollBar.h"

namespace ZuiLib {

/////////////////////////////////////////////////////////////////////////////////////
//
//

ZListView::ZListView() : m_pCallback(NULL), m_bScrollSelect(false), m_iCurSel(-1), m_iExpandedItem(-1)
{
    m_pList = new ZRefCountedObject<ZListBody>(this);
    m_pHeader = new ZRefCountedObject<ZListHeader>();

    Add(m_pHeader);
    ZVerticalLayout::Add(m_pList);

    m_ListInfo.nColumns = 0;
    m_ListInfo.uFontSize = 13;
    m_ListInfo.uTextStyle = DT_VCENTER; // m_uTextStyle(DT_VCENTER | DT_END_ELLIPSIS)
    m_ListInfo.dwTextColor = 0xFF000000;
    m_ListInfo.dwBkColor = 0;
    m_ListInfo.bAlternateBk = false;
    m_ListInfo.dwSelectedTextColor = 0xFF000000;
    m_ListInfo.dwSelectedBkColor = 0xFFC1E3FF;
    m_ListInfo.dwHotTextColor = 0xFF000000;
    m_ListInfo.dwHotBkColor = 0xFFE9F5FF;
    m_ListInfo.dwDisabledTextColor = 0xFFCCCCCC;
    m_ListInfo.dwDisabledBkColor = 0xFFFFFFFF;
    m_ListInfo.dwLineColor = 0;
    m_ListInfo.bMultiExpandable = false;
    memset(&m_ListInfo.rcTextPadding,0, sizeof(m_ListInfo.rcTextPadding));
    memset(&m_ListInfo.rcColumn,0, sizeof(m_ListInfo.rcColumn));
}

const char* ZListView::GetClass() const
{
    return "ListUI";
}


void* ZListView::GetInterface(const char* pstrName)
{
	if(strcmp(pstrName, "List") == 0 ) return static_cast<ZListView*>(this);
    if(strcmp(pstrName, "IList") == 0 ) return static_cast<IListView*>(this);
    if(strcmp(pstrName, "IListOwner") == 0 ) return static_cast<IListOwner*>(this);
    return ZVerticalLayout::GetInterface(pstrName);
}

ZControl* ZListView::GetItemAt(int iIndex) const
{
    return m_pList->GetItemAt(iIndex);
}

int ZListView::GetItemIndex(ZControl* pControl) const
{
    if( pControl->GetInterface("ListHeader") != NULL ) return ZVerticalLayout::GetItemIndex(pControl);
    // We also need to recognize header sub-items
    if(strcmp(pControl->GetClass(), "ListHeaderItem") != 0 ) return m_pHeader->GetItemIndex(pControl);

    return m_pList->GetItemIndex(pControl);
}

bool ZListView::SetItemIndex(ZControl* pControl, int iIndex)
{
    if( pControl->GetInterface("ListHeader") != NULL ) 
		return ZVerticalLayout::SetItemIndex(pControl, iIndex);

    // We also need to recognize header sub-items
    if(strcmp(pControl->GetClass(), "ListHeaderItem") != 0 ) 
		return m_pHeader->SetItemIndex(pControl, iIndex);

    int iOrginIndex = m_pList->GetItemIndex(pControl);
    if( iOrginIndex == -1 ) return false;
    if( iOrginIndex == iIndex ) return true;

    IListItem* pSelectedListItem = NULL;
    if( m_iCurSel >= 0 ) pSelectedListItem = 
        static_cast<IListItem*>(GetItemAt(m_iCurSel)->GetInterface("ListItem"));
    if( !m_pList->SetItemIndex(pControl, iIndex) ) return false;
    int iMinIndex = zmin(iOrginIndex, iIndex);
    int iMaxIndex = zmax(iOrginIndex, iIndex);
    for(int i = iMinIndex; i < iMaxIndex + 1; ++i) {
        ZControl* p = m_pList->GetItemAt(i);
        IListItem* pListItem = static_cast<IListItem*>(p->GetInterface("ListItem"));
        if( pListItem != NULL ) {
            pListItem->SetIndex(i);
        }
    }
    if( m_iCurSel >= 0 && pSelectedListItem != NULL ) m_iCurSel = pSelectedListItem->GetIndex();
    return true;
}

int ZListView::GetCount() const
{
    return m_pList->GetCount();
}

bool ZListView::Add(ZControl* pControl)
{
    // Override the Add() method so we can add items specifically to
    // the intended widgets. Headers are assumed to be
    // answer the correct interface so we can add multiple list headers.
    if( pControl->GetInterface("ListHeader") != NULL ) {
        if( m_pHeader != pControl && m_pHeader->GetCount() == 0 ) {
			ZVerticalLayout::Remove(m_pHeader);
            m_pHeader = static_cast<ZListHeader*>(pControl);
        }
        m_ListInfo.nColumns = zmin(m_pHeader->GetCount(), UILIST_MAX_COLUMNS);
        return ZVerticalLayout::AddAt(pControl, 0);
    }
    // We also need to recognize header sub-items
    if( strcmp(pControl->GetClass(), "ListHeaderItem") != 0 ) {
        bool ret = m_pHeader->Add(pControl);
        m_ListInfo.nColumns = zmin(m_pHeader->GetCount(), UILIST_MAX_COLUMNS);
        return ret;
    }
    // The list items should know about us
    IListItem* pListItem = static_cast<IListItem*>(pControl->GetInterface("ListItem"));
    if( pListItem != NULL ) {
        pListItem->SetOwner(this);
        pListItem->SetIndex(GetCount());
    }
    return m_pList->Add(pControl);
}

bool ZListView::AddAt(ZControl* pControl, int iIndex)
{
    // Override the AddAt() method so we can add items specifically to
    // the intended widgets. Headers and are assumed to be
    // answer the correct interface so we can add multiple list headers.
    if( pControl->GetInterface("ListHeader") != NULL ) {
        if( m_pHeader != pControl && m_pHeader->GetCount() == 0 ) {
            ZVerticalLayout::Remove(m_pHeader);
            m_pHeader = static_cast<ZListHeader*>(pControl);
        }
        m_ListInfo.nColumns = zmin(m_pHeader->GetCount(), UILIST_MAX_COLUMNS);
        return ZVerticalLayout::AddAt(pControl, 0);
    }
    // We also need to recognize header sub-items
    if( strcmp(pControl->GetClass(), "ListHeaderItemUI") != 0 ) {
        bool ret = m_pHeader->AddAt(pControl, iIndex);
        m_ListInfo.nColumns = zmin(m_pHeader->GetCount(), UILIST_MAX_COLUMNS);
        return ret;
    }
    if (!m_pList->AddAt(pControl, iIndex)) return false;

    // The list items should know about us
    IListItem* pListItem = static_cast<IListItem*>(pControl->GetInterface("ListItem"));
    if( pListItem != NULL ) {
        pListItem->SetOwner(this);
        pListItem->SetIndex(iIndex);
    }

    for(int i = iIndex + 1; i < m_pList->GetCount(); ++i) {
        ZControl* p = m_pList->GetItemAt(i);
        pListItem = static_cast<IListItem*>(p->GetInterface("ListItem"));
        if( pListItem != NULL ) {
            pListItem->SetIndex(i);
        }
    }
    if( m_iCurSel >= iIndex ) m_iCurSel += 1;
    return true;
}

bool ZListView::Remove(ZControl* pControl)
{
    if( pControl->GetInterface("ListHeader") != NULL ) 
		return ZVerticalLayout::Remove(pControl);
    // We also need to recognize header sub-items
    if( strcmp(pControl->GetClass(), "ListHeaderItem") != 0 ) return m_pHeader->Remove(pControl);

    int iIndex = m_pList->GetItemIndex(pControl);
    if (iIndex == -1) return false;

    if (!m_pList->RemoveAt(iIndex)) return false;

    for(int i = iIndex; i < m_pList->GetCount(); ++i) {
        ZControl* p = m_pList->GetItemAt(i);
        IListItem* pListItem = static_cast<IListItem*>(p->GetInterface("ListItem"));
        if( pListItem != NULL ) {
            pListItem->SetIndex(i);
        }
    }

    if( iIndex == m_iCurSel && m_iCurSel >= 0 ) {
        int iSel = m_iCurSel;
        m_iCurSel = -1;
        SelectItem(FindSelectable(iSel, false));
    }
    else if( iIndex < m_iCurSel ) m_iCurSel -= 1;
    return true;
}

bool ZListView::RemoveAt(int iIndex)
{
    if (!m_pList->RemoveAt(iIndex)) return false;

    for(int i = iIndex; i < m_pList->GetCount(); ++i) {
        ZControl* p = m_pList->GetItemAt(i);
        IListItem* pListItem = static_cast<IListItem*>(p->GetInterface("ListItem"));
        if( pListItem != NULL ) pListItem->SetIndex(i);
    }

    if( iIndex == m_iCurSel && m_iCurSel >= 0 ) {
        int iSel = m_iCurSel;
        m_iCurSel = -1;
        SelectItem(FindSelectable(iSel, false));
    }
    else if( iIndex < m_iCurSel ) m_iCurSel -= 1;
    return true;
}

void ZListView::RemoveAll()
{
    m_iCurSel = -1;
    m_iExpandedItem = -1;
    m_pList->RemoveAll();
}

void ZListView::SetPos(RECT rc)
{
    ZVerticalLayout::SetPos(rc);
    if( m_pHeader == NULL ) return;
    // Determine general list information and the size of header columns
    m_ListInfo.nColumns = zmin(m_pHeader->GetCount(), UILIST_MAX_COLUMNS);
    // The header/columns may or may not be visible at runtime. In either case
    // we should determine the correct dimensions...

    if( !m_pHeader->IsVisible() ) {
        for( int it = 0; it < m_pHeader->GetCount(); it++ ) {
            static_cast<ZControl*>(m_pHeader->GetItemAt(it))->SetInternVisible(true);
        }
        m_pHeader->SetPos(ZRect(rc.left, 0, rc.right, 0).GetCRect());
    }
    int iOffset = m_pList->GetScrollPos().cx;
    for( int i = 0; i < m_ListInfo.nColumns; i++ ) {
        ZControl* pControl = static_cast<ZControl*>(m_pHeader->GetItemAt(i));
        if( !pControl->IsVisible() ) continue;
        if( pControl->IsFloat() ) continue;

        RECT rcPos = pControl->GetPos();
        if( iOffset > 0 ) {
            rcPos.left -= iOffset;
            rcPos.right -= iOffset;
            pControl->SetPos(rcPos);
        }
        m_ListInfo.rcColumn[i] = pControl->GetPos();
    }
    if( !m_pHeader->IsVisible() ) {
        for( int it = 0; it < m_pHeader->GetCount(); it++ ) {
            static_cast<ZControl*>(m_pHeader->GetItemAt(it))->SetInternVisible(false);
        }
    }
}

void ZListView::DoEvent(TEventUI& event)
{
    if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) {
        if( m_pParent != NULL ) 
			m_pParent->DoEvent(event);
        else 
			ZVerticalLayout::DoEvent(event);
        return;
    }

    if( event.Type == UIEVENT_SETFOCUS ) 
    {
        m_bFocused = true;
        return;
    }
    if( event.Type == UIEVENT_KILLFOCUS ) 
    {
        m_bFocused = false;
        return;
    }

    switch( event.Type ) {
    case UIEVENT_KEYDOWN:
        {
            switch (event.chKey)
            {
            case SDLK_UP:
                SelectItem(FindSelectable(m_iCurSel - 1, false), true);
                return;
            case  SDLK_DOWN:
                SelectItem(FindSelectable(m_iCurSel + 1, true), true);
                return;
            case  SDLK_PAGEUP:
                PageUp();
                return;
            case  SDLK_PAGEDOWN:
                PageDown();
                return;
            case  SDLK_HOME:
                SelectItem(FindSelectable(0, false), true);
                return;
            case  SDLK_END:
                SelectItem(FindSelectable(GetCount() - 1, true), true);
                return;
            case  SDLK_RETURN:
                if (m_iCurSel != -1) GetItemAt(m_iCurSel)->Activate();
                return;
            }
        }
        break;
    case UIEVENT_SCROLLWHEEL:
        {
            switch (event.wParam) {
            case SBT_LINEUP:
                if (m_bScrollSelect) 
                    SelectItem(FindSelectable(m_iCurSel - 1, false), true);
                else 
                    LineUp();
                return;
            case SBT_LINEDOWN:
                if (m_bScrollSelect) 
                    SelectItem(FindSelectable(m_iCurSel + 1, true), true);
                else 
                    LineDown();
                return;
            }
        }
        break;
    }
    ZVerticalLayout::DoEvent(event);
}

ZListHeader* ZListView::GetHeader() const
{
    return m_pHeader;
}

ZContainer* ZListView::GetList() const
{
    return m_pList;
}

bool ZListView::GetScrollSelect()
{
    return m_bScrollSelect;
}

void ZListView::SetScrollSelect(bool bScrollSelect)
{
    m_bScrollSelect = bScrollSelect;
}

int ZListView::GetCurSel() const
{
    return m_iCurSel;
}

bool ZListView::SelectItem(int iIndex, bool bTakeFocus)
{
    if( iIndex == m_iCurSel ) return true;

    int iOldSel = m_iCurSel;
    // We should first unselect the currently selected item
    if( m_iCurSel >= 0 ) {
        ZControl* pControl = GetItemAt(m_iCurSel);
        if( pControl != NULL) {
            IListItem* pListItem = static_cast<IListItem*>(pControl->GetInterface("ListItem"));
            if( pListItem != NULL ) pListItem->Select(false);
        }

        m_iCurSel = -1;
    }
    if( iIndex < 0 ) return false;

    ZControl* pControl = GetItemAt(iIndex);
    if( pControl == NULL ) return false;
    if( !pControl->IsVisible() ) return false;
    if( !pControl->IsEnabled() ) return false;

    IListItem* pListItem = static_cast<IListItem*>(pControl->GetInterface("ListItem"));
    if( pListItem == NULL ) return false;
    m_iCurSel = iIndex;
    if( !pListItem->Select(true) ) {
        m_iCurSel = -1;
        return false;
    }
    EnsureVisible(m_iCurSel);
    if( bTakeFocus ) pControl->SetFocus();
    //if( m_pManager != NULL ) {
    //    m_pManager->SendNotify(this, _T("itemselect"), m_iCurSel, iOldSel);
    //}

    return true;
}

TListInfo* ZListView::GetListInfo()
{
    return &m_ListInfo;
}

int ZListView::GetChildPadding() const
{
    return m_pList->GetChildPadding();
}

void ZListView::SetChildPadding(int iPadding)
{
    m_pList->SetChildPadding(iPadding);
}

void ZListView::SetItemFont(const char* fontName)
{
    m_ListInfo.sFont = fontName;
    NeedUpdate();
}

void ZListView::SetItemTextStyle(uint32_t uStyle)
{
    m_ListInfo.uTextStyle = uStyle;
    NeedUpdate();
}

void ZListView::SetItemTextPadding(RECT rc)
{
    m_ListInfo.rcTextPadding = rc;
    NeedUpdate();
}

RECT ZListView::GetItemTextPadding() const
{
	return m_ListInfo.rcTextPadding;
}

void ZListView::SetItemTextColor(color_t dwTextColor)
{
    m_ListInfo.dwTextColor = dwTextColor;
    Invalidate();
}

void ZListView::SetItemBkColor(color_t dwBkColor)
{
    m_ListInfo.dwBkColor = dwBkColor;
    Invalidate();
}

void ZListView::SetItemBkImage(const char* pStrImage)
{
    m_ListInfo.sBkImage = pStrImage;
    Invalidate();
}

void ZListView::SetAlternateBk(bool bAlternateBk)
{
    m_ListInfo.bAlternateBk = bAlternateBk;
    Invalidate();
}

color_t ZListView::GetItemTextColor() const
{
	return m_ListInfo.dwTextColor;
}

color_t ZListView::GetItemBkColor() const
{
	return m_ListInfo.dwBkColor;
}

const char* ZListView::GetItemBkImage() const
{
	return m_ListInfo.sBkImage.drawString;
}

bool ZListView::IsAlternateBk() const
{
    return m_ListInfo.bAlternateBk;
}

void ZListView::SetSelectedItemTextColor(color_t dwTextColor)
{
    m_ListInfo.dwSelectedTextColor = dwTextColor;
    Invalidate();
}

void ZListView::SetSelectedItemBkColor(color_t dwBkColor)
{
    m_ListInfo.dwSelectedBkColor = dwBkColor;
    Invalidate();
}

void ZListView::SetSelectedItemImage(const char* pStrImage)
{
    m_ListInfo.sSelectedImage = pStrImage;
    Invalidate();
}

color_t ZListView::GetSelectedItemTextColor() const
{
	return m_ListInfo.dwSelectedTextColor;
}

color_t ZListView::GetSelectedItemBkColor() const
{
	return m_ListInfo.dwSelectedBkColor;
}

const char* ZListView::GetSelectedItemImage() const
{
	return m_ListInfo.sSelectedImage.drawString;
}

void ZListView::SetHotItemTextColor(color_t dwTextColor)
{
    m_ListInfo.dwHotTextColor = dwTextColor;
    Invalidate();
}

void ZListView::SetHotItemBkColor(color_t dwBkColor)
{
    m_ListInfo.dwHotBkColor = dwBkColor;
    Invalidate();
}

void ZListView::SetHotItemImage(const char* pStrImage)
{
    m_ListInfo.sHotImage = pStrImage;
    Invalidate();
}

color_t ZListView::GetHotItemTextColor() const
{
	return m_ListInfo.dwHotTextColor;
}
color_t ZListView::GetHotItemBkColor() const
{
	return m_ListInfo.dwHotBkColor;
}

const char* ZListView::GetHotItemImage() const
{
	return m_ListInfo.sHotImage.drawString;
}

void ZListView::SetDisabledItemTextColor(color_t dwTextColor)
{
    m_ListInfo.dwDisabledTextColor = dwTextColor;
    Invalidate();
}

void ZListView::SetDisabledItemBkColor(color_t dwBkColor)
{
    m_ListInfo.dwDisabledBkColor = dwBkColor;
    Invalidate();
}

void ZListView::SetDisabledItemImage(const char* pStrImage)
{
    m_ListInfo.sDisabledImage = pStrImage;
    Invalidate();
}

color_t ZListView::GetDisabledItemTextColor() const
{
	return m_ListInfo.dwDisabledTextColor;
}

color_t ZListView::GetDisabledItemBkColor() const
{
	return m_ListInfo.dwDisabledBkColor;
}

const char* ZListView::GetDisabledItemImage() const
{
	return m_ListInfo.sDisabledImage.drawString;
}

color_t ZListView::GetItemLineColor() const
{
	return m_ListInfo.dwLineColor;
}

void ZListView::SetItemLineColor(color_t dwLineColor)
{
    m_ListInfo.dwLineColor = dwLineColor;
    Invalidate();
}

void ZListView::SetMultiExpanding(bool bMultiExpandable)
{
    m_ListInfo.bMultiExpandable = bMultiExpandable;
}

bool ZListView::ExpandItem(int iIndex, bool bExpand /*= true*/)
{
    if( m_iExpandedItem >= 0 && !m_ListInfo.bMultiExpandable) {
        ZControl* pControl = GetItemAt(m_iExpandedItem);
        if( pControl != NULL ) {
            IListItem* pItem = static_cast<IListItem*>(pControl->GetInterface("ListItem"));
            if( pItem != NULL ) pItem->Expand(false);
        }
        m_iExpandedItem = -1;
    }
    if( bExpand ) {
        ZControl* pControl = GetItemAt(iIndex);
        if( pControl == NULL ) return false;
        if( !pControl->IsVisible() ) return false;
        IListItem* pItem = static_cast<IListItem*>(pControl->GetInterface("ListItem"));
        if( pItem == NULL ) return false;
        m_iExpandedItem = iIndex;
        if( !pItem->Expand(true) ) {
            m_iExpandedItem = -1;
            return false;
        }
    }
    NeedUpdate();
    return true;
}

int ZListView::GetExpandedItem() const
{
    return m_iExpandedItem;
}

void ZListView::EnsureVisible(int iIndex)
{
    if( m_iCurSel < 0 ) return;
    RECT rcItem = m_pList->GetItemAt(iIndex)->GetPos();
    RECT rcList = m_pList->GetPos();
    RECT rcListInset = m_pList->GetInset();

    rcList.left += rcListInset.left;
    rcList.top += rcListInset.top;
    rcList.right -= rcListInset.right;
    rcList.bottom -= rcListInset.bottom;

    ZScrollBar* pHorizontalScrollBar = m_pList->GetHorizontalScrollBar();
    if( pHorizontalScrollBar && pHorizontalScrollBar->IsVisible() ) rcList.bottom -= pHorizontalScrollBar->GetFixedHeight();

    int iPos = m_pList->GetScrollPos().cy;
    if( rcItem.top >= rcList.top && rcItem.bottom < rcList.bottom ) return;
    int dx = 0;
    if( rcItem.top < rcList.top ) dx = rcItem.top - rcList.top;
    if( rcItem.bottom > rcList.bottom ) dx = rcItem.bottom - rcList.bottom;
    Scroll(0, dx);
}

void ZListView::Scroll(int dx, int dy)
{
    if( dx == 0 && dy == 0 ) return;
    SIZE sz = m_pList->GetScrollPos();
	SIZE temp = { sz.cx + dx, sz.cy + dy };
    m_pList->SetScrollPos(temp);
}

void ZListView::SetAttribute(const char* pstrName, const char* pstrValue)
{
    if( strcmp(pstrName, "header") == 0 ) 
		GetHeader()->SetVisible(strcmp(pstrValue, "hidden") != 0);
    else if( strcmp(pstrName, "headerbkimage") == 0 ) 
		GetHeader()->SetBkImage(pstrValue);
    else if( strcmp(pstrName, "scrollselect") == 0 ) 
		SetScrollSelect(strcmp(pstrValue, "true") == 0);
    else if( strcmp(pstrName, "multiexpanding") == 0 ) 
		SetMultiExpanding(strcmp(pstrValue, "true") == 0);
    else if( strcmp(pstrName, "itemfont") == 0 ) 
		m_ListInfo.sFont = pstrValue;
    else if( strcmp(pstrName, "itemalign") == 0 ) {
        if( strstr(pstrValue, "left") != NULL ) {
            m_ListInfo.uTextStyle &= ~(DT_CENTER | DT_RIGHT);
            m_ListInfo.uTextStyle |= DT_LEFT;
        }
        if(strstr(pstrValue, "center") != NULL ) {
            m_ListInfo.uTextStyle &= ~(DT_LEFT | DT_RIGHT);
            m_ListInfo.uTextStyle |= DT_CENTER;
        }
        if(strstr(pstrValue, "right") != NULL ) {
            m_ListInfo.uTextStyle &= ~(DT_LEFT | DT_CENTER);
            m_ListInfo.uTextStyle |= DT_RIGHT;
        }
    }
    else if( strcmp(pstrName, "itemendellipsis") == 0 ) {
        if( strcmp(pstrValue, "true") == 0 ) m_ListInfo.uTextStyle |= DT_END_ELLIPSIS;
        else m_ListInfo.uTextStyle &= ~DT_END_ELLIPSIS;
    }    
    if( strcmp(pstrName, "itemtextpadding") == 0 ) {
        RECT rcTextPadding = { 0 };
		ParserRect(&rcTextPadding, pstrValue);
        SetItemTextPadding(rcTextPadding);
    }
    else if( strcmp(pstrName, "itemtextcolor") == 0 ) {
        SetItemTextColor(TextToColor(pstrValue));
    }
    else if( strcmp(pstrName, "itembkcolor") == 0 ) {
        SetItemBkColor(TextToColor(pstrValue));
    }
    else if( strcmp(pstrName, "itembkimage") == 0 ) 
		SetItemBkImage(pstrValue);
    else if( strcmp(pstrName, "itemaltbk") == 0 ) 
		SetAlternateBk(strcmp(pstrValue, "true") == 0);
    else if( strcmp(pstrName, "itemselectedtextcolor") == 0 ) {
        SetSelectedItemTextColor(TextToColor(pstrValue));
    }
    else if( strcmp(pstrName, "itemselectedbkcolor") == 0 ) {
        SetSelectedItemBkColor(TextToColor(pstrValue));
    }
    else if( strcmp(pstrName, "itemselectedimage") == 0 ) 
		SetSelectedItemImage(pstrValue);
    else if( strcmp(pstrName, "itemhottextcolor") == 0 ) {
        SetHotItemTextColor(TextToColor(pstrValue));
    }
    else if( strcmp(pstrName, "itemhotbkcolor") == 0 ) {
        SetHotItemBkColor(TextToColor(pstrValue));
    }
    else if( strcmp(pstrName, "itemhotimage") == 0 )
		SetHotItemImage(pstrValue);
    else if( strcmp(pstrName, "itemdisabledtextcolor") == 0 ) {
        SetDisabledItemTextColor(TextToColor(pstrValue));
    }
    else if( strcmp(pstrName, "itemdisabledbkcolor") == 0 ) {
        SetDisabledItemBkColor(TextToColor(pstrValue));
    }
    else if( strcmp(pstrName,"itemdisabledimage") == 0 ) 
		SetDisabledItemImage(pstrValue);
    else if( strcmp(pstrName,"itemlinecolor") == 0 ) {
        SetItemLineColor(TextToColor(pstrValue));
    }
    else 
		ZVerticalLayout::SetAttribute(pstrName, pstrValue);
}

IListCallback* ZListView::GetTextCallback() const
{
    return m_pCallback;
}

void ZListView::SetTextCallback(IListCallback* pCallback)
{
    m_pCallback = pCallback;
}

SIZE ZListView::GetScrollPos() const
{
    return m_pList->GetScrollPos();
}

SIZE ZListView::GetScrollRange() const
{
    return m_pList->GetScrollRange();
}

void ZListView::SetScrollPos(SIZE szPos)
{
    m_pList->SetScrollPos(szPos);
}

void ZListView::LineUp()
{
    m_pList->LineUp();
}

void ZListView::LineDown()
{
    m_pList->LineDown();
}

void ZListView::PageUp()
{
    m_pList->PageUp();
}

void ZListView::PageDown()
{
    m_pList->PageDown();
}

void ZListView::HomeUp()
{
    m_pList->HomeUp();
}

void ZListView::EndDown()
{
    m_pList->EndDown();
}

void ZListView::LineLeft()
{
    m_pList->LineLeft();
}

void ZListView::LineRight()
{
    m_pList->LineRight();
}

void ZListView::PageLeft()
{
    m_pList->PageLeft();
}

void ZListView::PageRight()
{
    m_pList->PageRight();
}

void ZListView::HomeLeft()
{
    m_pList->HomeLeft();
}

void ZListView::EndRight()
{
    m_pList->EndRight();
}

void ZListView::EnableScrollBar(bool bEnableVertical, bool bEnableHorizontal)
{
    m_pList->EnableScrollBar(bEnableVertical, bEnableHorizontal);
}

ZScrollBar* ZListView::GetVerticalScrollBar() const
{
    return m_pList->GetVerticalScrollBar();
}

ZScrollBar* ZListView::GetHorizontalScrollBar() const
{
    return m_pList->GetHorizontalScrollBar();
}

/////////////////////////////////////////////////////////////////////////////////////
//
//


ZListBody::ZListBody(ZListView* pOwner) : m_pOwner(pOwner)
{
    assert(m_pOwner);
	memset(&m_rcInset, 0, sizeof(m_rcInset));
}

void ZListBody::SetScrollPos(SIZE szPos)
{
    int cx = 0;
    int cy = 0;
    if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) {
        int iLastScrollPos = m_pVerticalScrollBar->GetScrollPos();
        m_pVerticalScrollBar->SetScrollPos(szPos.cy);
        cy = m_pVerticalScrollBar->GetScrollPos() - iLastScrollPos;
    }

    if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) {
        int iLastScrollPos = m_pHorizontalScrollBar->GetScrollPos();
        m_pHorizontalScrollBar->SetScrollPos(szPos.cx);
        cx = m_pHorizontalScrollBar->GetScrollPos() - iLastScrollPos;
    }

    if( cx == 0 && cy == 0 ) return;

    RECT rcPos;
    for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) {
        ZControl* pControl = static_cast<ZControl*>(m_items[it2]);
        if( !pControl->IsVisible() ) continue;
        if( pControl->IsFloat() ) continue;

        rcPos = pControl->GetPos();
        rcPos.left -= cx;
        rcPos.right -= cx;
        rcPos.top -= cy;
        rcPos.bottom -= cy;
        pControl->SetPos(rcPos);
    }

    Invalidate();

    if( cx != 0 && m_pOwner ) {
        ZListHeader* pHeader = m_pOwner->GetHeader();
        if( pHeader == NULL ) return;
        TListInfo* pInfo = m_pOwner->GetListInfo();
        pInfo->nColumns = zmin(pHeader->GetCount(), UILIST_MAX_COLUMNS);

        if( !pHeader->IsVisible() ) {
            for( int it = 0; it < pHeader->GetCount(); it++ ) {
                static_cast<ZControl*>(pHeader->GetItemAt(it))->SetInternVisible(true);
            }
        }
        for( int i = 0; i < pInfo->nColumns; i++ ) {
            ZControl* pControl = static_cast<ZControl*>(pHeader->GetItemAt(i));
            if( !pControl->IsVisible() ) continue;
            if( pControl->IsFloat() ) continue;

            RECT rcPos = pControl->GetPos();
            rcPos.left -= cx;
            rcPos.right -= cx;
            pControl->SetPos(rcPos);
            pInfo->rcColumn[i] = pControl->GetPos();
        }
        if( !pHeader->IsVisible() ) {
            for( int it = 0; it < pHeader->GetCount(); it++ ) {
                static_cast<ZControl*>(pHeader->GetItemAt(it))->SetInternVisible(false);
            }
        }
    }
}

void ZListBody::SetPos(RECT rc)
{
    ZControl::SetPos(rc);
    rc = m_rcItem;

    // Adjust for inset
    rc.left += m_rcInset.left;
    rc.top += m_rcInset.top;
    rc.right -= m_rcInset.right;
    rc.bottom -= m_rcInset.bottom;
    if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth();
    if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight();

    // Determine the minimum size
    SIZE szAvailable = { rc.right - rc.left, rc.bottom - rc.top };
    if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) 
        szAvailable.cx += m_pHorizontalScrollBar->GetScrollRange();

    int cxNeeded = 0;
    int nAdjustables = 0;
    int cyFixed = 0;
    int nEstimateNum = 0;
    for( int it1 = 0; it1 < m_items.GetSize(); it1++ ) {
        ZControl* pControl = static_cast<ZControl*>(m_items[it1]);
        if( !pControl->IsVisible() ) continue;
        if( pControl->IsFloat() ) continue;
        SIZE sz = pControl->EstimateSize(szAvailable);
        if( sz.cy == 0 ) {
            nAdjustables++;
        }
        else {
            if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight();
            if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight();
        }
        cyFixed += sz.cy + pControl->GetPadding().top + pControl->GetPadding().bottom;

        RECT rcPadding = pControl->GetPadding();
        sz.cx = zmax(sz.cx, 0);
        if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth();
        if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth();
        cxNeeded = zmax(cxNeeded, sz.cx);
        nEstimateNum++;
    }
    cyFixed += (nEstimateNum - 1) * m_iChildPadding;

    if( m_pOwner ) {
        ZListHeader* pHeader = m_pOwner->GetHeader();
        if( pHeader != NULL && pHeader->GetCount() > 0 ) {
			SIZE sz = { rc.right - rc.left, rc.bottom - rc.top };
            cxNeeded = zmax(0, pHeader->EstimateSize(sz).cx);
        }
    }

    // Place elements
    int cyNeeded = 0;
    int cyExpand = 0;
    if( nAdjustables > 0 ) cyExpand = zmax(0, (szAvailable.cy - cyFixed) / nAdjustables);
    // Position the elements
    SIZE szRemaining = szAvailable;
    int iPosY = rc.top;
    if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) {
        iPosY -= m_pVerticalScrollBar->GetScrollPos();
    }
    int iPosX = rc.left;
    if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) {
        iPosX -= m_pHorizontalScrollBar->GetScrollPos();
    }
    int iAdjustable = 0;
    int cyFixedRemaining = cyFixed;
    for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) {
        ZControl* pControl = static_cast<ZControl*>(m_items[it2]);
        if( !pControl->IsVisible() ) continue;
        if( pControl->IsFloat() ) {
            SetFloatPos(it2);
            continue;
        }

        RECT rcPadding = pControl->GetPadding();
        szRemaining.cy -= rcPadding.top;
        SIZE sz = pControl->EstimateSize(szRemaining);
        if( sz.cy == 0 ) {
            iAdjustable++;
            sz.cy = cyExpand;
            // Distribute remaining to last element (usually round-off left-overs)
            if( iAdjustable == nAdjustables ) {
                sz.cy = zmax(0, szRemaining.cy - rcPadding.bottom - cyFixedRemaining);
            } 
            if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight();
            if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight();
        }
        else {
            if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight();
            if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight();
            cyFixedRemaining -= sz.cy;
        }

        sz.cx = zmax(cxNeeded, szAvailable.cx - rcPadding.left - rcPadding.right);

        if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth();
        if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth();

        RECT rcCtrl = { iPosX + rcPadding.left, iPosY + rcPadding.top, iPosX + rcPadding.left + sz.cx, iPosY + sz.cy + rcPadding.top + rcPadding.bottom };
        pControl->SetPos(rcCtrl);

        iPosY += sz.cy + m_iChildPadding + rcPadding.top + rcPadding.bottom;
        cyNeeded += sz.cy + rcPadding.top + rcPadding.bottom;
        szRemaining.cy -= sz.cy + m_iChildPadding + rcPadding.bottom;
    }
    cyNeeded += (nEstimateNum - 1) * m_iChildPadding;

    if( m_pHorizontalScrollBar != NULL ) {
        if( cxNeeded > rc.right - rc.left ) {
            if( m_pHorizontalScrollBar->IsVisible() ) {
                m_pHorizontalScrollBar->SetScrollRange(cxNeeded - (rc.right - rc.left));
            }
            else {
                m_pHorizontalScrollBar->SetVisible(true);
                m_pHorizontalScrollBar->SetScrollRange(cxNeeded - (rc.right - rc.left));
                m_pHorizontalScrollBar->SetScrollPos(0);
                rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight();
            }
        }
        else {
            if( m_pHorizontalScrollBar->IsVisible() ) {
                m_pHorizontalScrollBar->SetVisible(false);
                m_pHorizontalScrollBar->SetScrollRange(0);
                m_pHorizontalScrollBar->SetScrollPos(0);
                rc.bottom += m_pHorizontalScrollBar->GetFixedHeight();
            }
        }
    }

    // Process the scrollbar
    ProcessScrollBar(rc, cxNeeded, cyNeeded);
}

void ZListBody::DoEvent(TEventUI& event)
{
    if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) {
        if( m_pOwner != NULL ) m_pOwner->DoEvent(event);
        else ZControl::DoEvent(event);
        return;
    }

    if( m_pOwner != NULL ) m_pOwner->DoEvent(event); else ZControl::DoEvent(event);
}

/////////////////////////////////////////////////////////////////////////////////////
//
//

ZListHeader::ZListHeader()
{
}

const char* ZListHeader::GetClass() const
{
    return "ListHeader";
}

void* ZListHeader::GetInterface(const char* pstrName)
{
    if( strcmp(pstrName, "ListHeader") == 0 ) 
		return this;
    return ZHorizontalLayout::GetInterface(pstrName);
}

SIZE ZListHeader::EstimateSize(SIZE szAvailable)
{
    SIZE cXY = {0, m_cxyFixed.cy};
	if( cXY.cy == 0 && m_pManager != NULL ) {
		for( int it = 0; it < m_items.GetSize(); it++ ) {
			cXY.cy = zmax(cXY.cy,static_cast<ZControl*>(m_items[it])->EstimateSize(szAvailable).cy);
		}
		int nMin = GetRenerder()->FindFont(NULL)->GetLineHeight() + 6;
		cXY.cy = zmax(cXY.cy,nMin);
	}

    for( int it = 0; it < m_items.GetSize(); it++ ) {
        cXY.cx +=  static_cast<ZControl*>(m_items[it])->EstimateSize(szAvailable).cx;
    }

    return cXY;
}

/////////////////////////////////////////////////////////////////////////////////////
//
//

ZListHeaderItem::ZListHeaderItem() : m_bDragable(true), m_uButtonState(0), m_iSepWidth(4), 
m_uFontSize(13),m_uTextStyle(DT_VCENTER | DT_CENTER | DT_SINGLELINE), m_dwTextColor(0)
{
	SetTextPadding(ZRect(2, 0, 2, 0).GetCRect());
    ptLastMouse.x = ptLastMouse.y = 0;
    SetMinWidth(16);
}

const char* ZListHeaderItem::GetClass() const
{
    return "ListHeaderItem";
}

void* ZListHeaderItem::GetInterface(const char* pstrName)
{
    if( strcmp(pstrName, "ListHeaderItem") == 0 ) 
		return this;
	else
		return ZControl::GetInterface(pstrName);
}



void ZListHeaderItem::SetEnabled(bool bEnable)
{
    ZControl::SetEnabled(bEnable);
    if( !IsEnabled() ) {
        m_uButtonState = 0;
    }
}

bool ZListHeaderItem::IsDragable() const
{
	return m_bDragable;
}

void ZListHeaderItem::SetDragable(bool bDragable)
{
    m_bDragable = bDragable;
    if ( !m_bDragable ) m_uButtonState &= ~UISTATE_CAPTURED;
}

Uint32 ZListHeaderItem::GetSepWidth() const
{
	return m_iSepWidth;
}

void ZListHeaderItem::SetSepWidth(int iWidth)
{
    m_iSepWidth = iWidth;
}

Uint32 ZListHeaderItem::GetTextStyle() const
{
	return m_uTextStyle;
}

void ZListHeaderItem::SetTextStyle(Uint32 uStyle)
{
    m_uTextStyle = uStyle;
    Invalidate();
}

color_t ZListHeaderItem::GetTextColor() const
{
	return m_dwTextColor;
}


void ZListHeaderItem::SetTextColor(color_t dwTextColor)
{
    m_dwTextColor = dwTextColor;
}

RECT ZListHeaderItem::GetTextPadding() const
{
	return m_rcTextPadding;
}

void ZListHeaderItem::SetTextPadding(RECT rc)
{
	m_rcTextPadding = rc;
	Invalidate();
}

void ZListHeaderItem::SetFont(const char* font)
{
    m_sFont = font;
}


const char* ZListHeaderItem::GetNormalImage() const
{
	return m_sNormalImage.drawString;
}

void ZListHeaderItem::SetNormalImage(const char* pStrImage)
{
    m_sNormalImage = pStrImage;
    Invalidate();
}

const char* ZListHeaderItem::GetHotImage() const
{
    return m_sHotImage.drawString;
}

void ZListHeaderItem::SetHotImage(const char* pStrImage)
{
    m_sHotImage = pStrImage;
    Invalidate();
}

const char* ZListHeaderItem::GetPushedImage() const
{
    return m_sPushedImage.drawString;
}

void ZListHeaderItem::SetPushedImage(const char* pStrImage)
{
    m_sPushedImage = pStrImage;
    Invalidate();
}

const char* ZListHeaderItem::GetFocusedImage() const
{
    return m_sFocusedImage.drawString;
}

void ZListHeaderItem::SetFocusedImage(const char* pStrImage)
{
    m_sFocusedImage = pStrImage;
    Invalidate();
}

const char* ZListHeaderItem::GetSepImage() const
{
    return m_sSepImage.drawString;
}

void ZListHeaderItem::SetSepImage(const char* pStrImage)
{
    m_sSepImage = pStrImage;
    Invalidate();
}

void ZListHeaderItem::SetAttribute(const char* pstrName, const char* pstrValue)
{
    if( strcmp(pstrName, "dragable") == 0 ) 
		SetDragable(strcmp(pstrValue, "true") == 0);
    else if( strcmp(pstrName, "sepwidth") == 0 ) 
		SetSepWidth(atoi(pstrValue));
    else if( strcmp(pstrName, "align") == 0 ) {
        if( strstr(pstrValue, "left") != NULL ) {
            m_uTextStyle &= ~(DT_CENTER | DT_RIGHT);
            m_uTextStyle |= DT_LEFT;
        }
        if(strstr(pstrValue, "center") != NULL ) {
            m_uTextStyle &= ~(DT_LEFT | DT_RIGHT);
            m_uTextStyle |= DT_CENTER;
        }
        if(strstr(pstrValue, "right") != NULL ) {
            m_uTextStyle &= ~(DT_LEFT | DT_CENTER);
            m_uTextStyle |= DT_RIGHT;
        }
    }
    else if( strcmp(pstrName, "endellipsis") == 0 ) {
        if( strcmp(pstrValue, "true") == 0 ) 
			m_uTextStyle |= DT_END_ELLIPSIS;
        else 
			m_uTextStyle &= ~DT_END_ELLIPSIS;
    }    
    else if( strcmp(pstrName, "font") == 0 ) 
		SetFont(pstrValue);
    else if( strcmp(pstrName, "textcolor") == 0 ) {
        SetTextColor(TextToColor(pstrValue));
    }
	else if( strcmp(pstrName, "textpadding") == 0 ) {
		RECT rcTextPadding = { 0 };
		ParserRect(&rcTextPadding, pstrValue);
		SetTextPadding(rcTextPadding);
	}
    else if( strcmp(pstrName, "normalimage") == 0 ) 
		SetNormalImage(pstrValue);
    else if( strcmp(pstrName, "hotimage") == 0 ) 
		SetHotImage(pstrValue);
    else if( strcmp(pstrName, "pushedimage") == 0 ) 
		SetPushedImage(pstrValue);
    else if( strcmp(pstrName, "focusedimage") == 0 ) 
		SetFocusedImage(pstrValue);
    else if( strcmp(pstrName, "sepimage") == 0 ) 
		SetSepImage(pstrValue);
    else 
		ZControl::SetAttribute(pstrName, pstrValue);
}

void ZListHeaderItem::DoEvent(TEventUI& event)
{
    if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) {
        if( m_pParent != NULL ) m_pParent->DoEvent(event);
        else ZControl::DoEvent(event);
        return;
    }

    if( event.Type == UIEVENT_SETFOCUS ) 
    {
        Invalidate();
    }
    if( event.Type == UIEVENT_KILLFOCUS ) 
    {
        Invalidate();
    }
    if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK )
    {
        if( !IsEnabled() ) return;
        RECT rcSeparator = GetThumbRect();
        if( PtInRect(&rcSeparator, event.ptMouse) ) {
            if( m_bDragable ) {
                m_uButtonState |= UISTATE_CAPTURED;
                ptLastMouse = event.ptMouse;
            }
        }
        else {
            m_uButtonState |= UISTATE_PUSHED;
            //m_pManager->SendNotify(this, _T("headerclick"));
            Invalidate();
        }
        return;
    }
    if( event.Type == UIEVENT_BUTTONUP )
    {
        if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) {
            m_uButtonState &= ~UISTATE_CAPTURED;
            if( GetParent() ) 
                GetParent()->NeedParentUpdate();
        }
        else if( (m_uButtonState & UISTATE_PUSHED) != 0 ) {
            m_uButtonState &= ~UISTATE_PUSHED;
            Invalidate();
        }
        return;
    }
    if( event.Type == UIEVENT_MOUSEMOVE )
    {
        if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) {
            RECT rc = m_rcItem;
            if( m_iSepWidth >= 0 ) {
                rc.right -= ptLastMouse.x - event.ptMouse.x;
            }
            else {
                rc.left -= ptLastMouse.x - event.ptMouse.x;
            }
            
            if( rc.right - rc.left > GetMinWidth() ) {
                m_cxyFixed.cx = rc.right - rc.left;
                ptLastMouse = event.ptMouse;
                if( GetParent() ) 
                    GetParent()->NeedParentUpdate();
            }
        }
        return;
    }
    if( event.Type == UIEVENT_SETCURSOR )
    {
        RECT rcSeparator = GetThumbRect();
        if( IsEnabled() && m_bDragable && PtInRect(&rcSeparator, event.ptMouse) ) {
			GetWindow()->SetCursor(SDL_SYSTEM_CURSOR_SIZEWE);
            return;
        }
    }
    if( event.Type == UIEVENT_MOUSEENTER )
    {
        if( IsEnabled() ) {
            m_uButtonState |= UISTATE_HOT;
            Invalidate();
        }
        return;
    }
    if( event.Type == UIEVENT_MOUSELEAVE )
    {
        if( IsEnabled() ) {
            m_uButtonState &= ~UISTATE_HOT;
            Invalidate();
        }
        return;
    }
    ZControl::DoEvent(event);
}

SIZE ZListHeaderItem::EstimateSize(SIZE szAvailable)
{
    if( m_cxyFixed.cy == 0 ) 
		return MakeSize(m_cxyFixed.cx, 12 + 14);
	else
		return ZControl::EstimateSize(szAvailable);
}

RECT ZListHeaderItem::GetThumbRect() const
{
    if( m_iSepWidth >= 0 ) 
		return MakeRect(m_rcItem.right - m_iSepWidth, m_rcItem.top, m_rcItem.right, m_rcItem.bottom);
    else 
		return MakeRect(m_rcItem.left, m_rcItem.top, m_rcItem.left - m_iSepWidth, m_rcItem.bottom);
}

void ZListHeaderItem::PaintStatusImage(ZRenderer* render)
{
    if( IsFocused() ) m_uButtonState |= UISTATE_FOCUSED;
    else m_uButtonState &= ~ UISTATE_FOCUSED;

    if( (m_uButtonState & UISTATE_PUSHED) != 0 ) {
        if( m_sPushedImage.IsEmpty() && !m_sNormalImage.IsEmpty() ) 
            DrawImage(render,&m_sNormalImage);
        if( !DrawImage(render, &m_sPushedImage) ) 
            m_sPushedImage.Clear();
    }
    else if( (m_uButtonState & UISTATE_HOT) != 0 ) {
        if( m_sHotImage.IsEmpty() && !m_sNormalImage.IsEmpty() ) 
            DrawImage(render, &m_sNormalImage);
        if( !DrawImage(render,&m_sHotImage) ) 
            m_sHotImage.Clear();
    }
    else if( (m_uButtonState & UISTATE_FOCUSED) != 0 ) {
        if( m_sFocusedImage.IsEmpty() && !m_sNormalImage.IsEmpty() ) 
            DrawImage(render, &m_sNormalImage);
        if( !DrawImage(render, &m_sFocusedImage) ) 
            m_sFocusedImage.Clear();
    }
    else {
        if( !m_sNormalImage.IsEmpty() ) {
            if( !DrawImage(render, &m_sNormalImage) )
                m_sNormalImage.Clear();
        }
    }

    if( !m_sSepImage.IsEmpty() ) {
        RECT rcThumb = GetThumbRect();
        rcThumb.left -= m_rcItem.left;
        rcThumb.top -= m_rcItem.top;
        rcThumb.right -= m_rcItem.left;
        rcThumb.bottom -= m_rcItem.top;

        m_sSepImage.dest = rcThumb;
        if( !DrawImage(render, &m_sSepImage) ) 
            m_sSepImage.Clear();
    }
}

void ZListHeaderItem::PaintText(ZRenderer* render)
{
    if( m_dwTextColor == 0 ) 
		m_dwTextColor = m_pManager->GetDefaultFontColor();

	RECT rcText = m_rcItem;
	rcText.left += m_rcTextPadding.left;
	rcText.top += m_rcTextPadding.top;
	rcText.right -= m_rcTextPadding.right;
	rcText.bottom -= m_rcTextPadding.bottom;
    if( m_sText.IsEmpty() ) 
		return;

	render->DrawText(m_sFont, m_sText, m_uFontSize,rcText,m_dwTextColor, m_uTextStyle);
}

/////////////////////////////////////////////////////////////////////////////////////
//
//

ZListElement::ZListElement() : 
m_iIndex(-1),
m_pOwner(NULL), 
m_bSelected(false),
m_uButtonState(0)
{
}

const char* ZListElement::GetClass() const
{
    return "ListElement";
}

void* ZListElement::GetInterface(const char* pstrName)
{
    if( strcmp(pstrName, "ListItem") == 0 ) 
		return static_cast<IListItem*>(this);
	if( strcmp(pstrName, "ListElement") == 0 ) 
		return static_cast<ZListElement*>(this);
    return ZControl::GetInterface(pstrName);
}

IListOwner* ZListElement::GetOwner()
{
    return m_pOwner;
}

void ZListElement::SetOwner(ZControl* pOwner)
{
    m_pOwner = static_cast<IListOwner*>(pOwner->GetInterface("IListOwner"));
}

void ZListElement::SetVisible(bool bVisible)
{
    ZControl::SetVisible(bVisible);
    if( !IsVisible() && m_bSelected)
    {
        m_bSelected = false;
        if( m_pOwner != NULL ) m_pOwner->SelectItem(-1);
    }
}

void ZListElement::SetEnabled(bool bEnable)
{
    ZControl::SetEnabled(bEnable);
    if( !IsEnabled() ) {
        m_uButtonState = 0;
    }
}

int ZListElement::GetIndex() const
{
    return m_iIndex;
}

void ZListElement::SetIndex(int iIndex)
{
    m_iIndex = iIndex;
}

void ZListElement::Invalidate()
{
    if( !IsVisible() ) return;

    if( GetParent() ) {
        ZContainer* pParentContainer = static_cast<ZContainer*>(GetParent()->GetInterface("Container"));
        if( pParentContainer ) {
            RECT rc = pParentContainer->GetPos();
            RECT rcInset = pParentContainer->GetInset();
            rc.left += rcInset.left;
            rc.top += rcInset.top;
            rc.right -= rcInset.right;
            rc.bottom -= rcInset.bottom;
            ZScrollBar* pVerticalScrollBar = pParentContainer->GetVerticalScrollBar();
            if( pVerticalScrollBar && pVerticalScrollBar->IsVisible() ) rc.right -= pVerticalScrollBar->GetFixedWidth();
            ZScrollBar* pHorizontalScrollBar = pParentContainer->GetHorizontalScrollBar();
            if( pHorizontalScrollBar && pHorizontalScrollBar->IsVisible() ) rc.bottom -= pHorizontalScrollBar->GetFixedHeight();

            RECT invalidateRc = m_rcItem;
            if( !IntersectRect(&invalidateRc, &m_rcItem, &rc) ) 
            {
                return;
            }

            ZControl* pParent = GetParent();
            RECT rcTemp;
            RECT rcParent;
            while( pParent = pParent->GetParent() )
            {
                rcTemp = invalidateRc;
                rcParent = pParent->GetPos();
                if( !IntersectRect(&invalidateRc, &rcTemp, &rcParent) ) 
                {
                    return;
                }
            }

            if( m_pManager != NULL ) m_pManager->Invalidate(invalidateRc);
        }
        else {
            ZControl::Invalidate();
        }
    }
    else {
        ZControl::Invalidate();
    }
}

bool ZListElement::Activate()
{
    if( !ZControl::Activate() ) return false;
    //if( m_pManager != NULL ) m_pManager->SendNotify(this, _T("itemactivate"));
    return true;
}

bool ZListElement::IsSelected() const
{
    return m_bSelected;
}

bool ZListElement::Select(bool bSelect)
{
    if( !IsEnabled() ) return false;
    if( bSelect == m_bSelected ) return true;
    m_bSelected = bSelect;
    if( bSelect && m_pOwner != NULL ) m_pOwner->SelectItem(m_iIndex);
    Invalidate();

    return true;
}

bool ZListElement::IsExpanded() const
{
    return false;
}

bool ZListElement::Expand(bool /*bExpand = true*/)
{
    return false;
}

void ZListElement::DoEvent(TEventUI& event)
{
    if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) {
        if( m_pOwner != NULL ) m_pOwner->DoEvent(event);
        else ZControl::DoEvent(event);
        return;
    }

    if( event.Type == UIEVENT_DBLCLICK )
    {
        if( IsEnabled() ) {
            Activate();
            Invalidate();
        }
        return;
    }
    if( event.Type == UIEVENT_KEYDOWN && IsEnabled() )
    {
        if( event.chKey == SDLK_RETURN ) {
            Activate();
            Invalidate();
            return;
        }
    }
    // An important twist: The list-item will send the event not to its immediate
    // parent but to the "attached" list. A list may actually embed several components
    // in its path to the item, but key-presses etc. needs to go to the actual list.
    if( m_pOwner != NULL ) m_pOwner->DoEvent(event); else ZControl::DoEvent(event);
}

void ZListElement::SetAttribute(const char* pstrName, const char* pstrValue)
{
    if( strcmp(pstrName, "selected") == 0 ) 
		Select();
    else 
		ZControl::SetAttribute(pstrName, pstrValue);
}

void ZListElement::DrawItemBk(ZRenderer* render, const RECT& rcItem)
{
    assert(m_pOwner);
    if( m_pOwner == NULL ) return;
    TListInfo* pInfo = m_pOwner->GetListInfo();
    color_t iBackColor = 0;
    if( !pInfo->bAlternateBk || m_iIndex % 2 == 0 ) iBackColor = pInfo->dwBkColor;
    if( (m_uButtonState & UISTATE_HOT) != 0 ) {
        iBackColor = pInfo->dwHotBkColor;
    }
    if( IsSelected() ) {
        iBackColor = pInfo->dwSelectedBkColor;
    }
    if( !IsEnabled() ) {
        iBackColor = pInfo->dwDisabledBkColor;
    }

    if ( iBackColor != 0 ) {
		render->SetDrawColor(iBackColor);
		render->FillRect(m_rcItem);
    }

    if( !IsEnabled() ) {
        if( !pInfo->sDisabledImage.IsEmpty() ) {
            if( !DrawImage(render,&pInfo->sDisabledImage) )
                pInfo->sDisabledImage.Clear();
            else 
                return;
        }
    }
    if( IsSelected() ) {
        if( !pInfo->sSelectedImage.IsEmpty() ) {
            if( !DrawImage(render, &pInfo->sSelectedImage) )
                pInfo->sSelectedImage.Clear();
            else 
                return;
        }
    }
    if( (m_uButtonState & UISTATE_HOT) != 0 ) {
        if( !pInfo->sHotImage.IsEmpty() ) {
            if( !DrawImage(render, &pInfo->sHotImage) ) 
                pInfo->sHotImage.Clear();
            else 
                return;
        }
    }

    if( !m_sBkImage.IsEmpty() ) {
        if( !pInfo->bAlternateBk || m_iIndex % 2 == 0 ) {
            DrawImage(render,&m_sBkImage);
        }
    }

    if( m_sBkImage.IsEmpty() ) {
        if( !pInfo->sBkImage.IsEmpty() ) {
            if( !DrawImage(render, &pInfo->sBkImage) ) 
                pInfo->sBkImage.Clear();
            else 
                return;
        }
    }

    if ( pInfo->dwLineColor != 0 ) {
        RECT rcLine = { m_rcItem.left, m_rcItem.bottom - 1, m_rcItem.right, m_rcItem.bottom - 1 };
		render->SetDrawColor(pInfo->dwLineColor);
		render->FillRect(rcLine);
    }
}

/////////////////////////////////////////////////////////////////////////////////////
//
//

ZListLabelElement::ZListLabelElement()
{
}

const char* ZListLabelElement::GetClass() const
{
    return "ListLabelElement";
}

void* ZListLabelElement::GetInterface(const char* pstrName)
{
    if( strcmp(pstrName, "ListLabelElement") == 0 ) 
		return static_cast<ZListLabelElement*>(this);
    return ZListElement::GetInterface(pstrName);
}

void ZListLabelElement::DoEvent(TEventUI& event)
{
    if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) {
        if( m_pOwner != NULL ) m_pOwner->DoEvent(event);
        else ZListElement::DoEvent(event);
        return;
    }

    if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_RBUTTONDOWN )
    {
        if( IsEnabled() ) {
            //m_pManager->SendNotify(this, _T("itemclick"));
            Select();
            Invalidate();
        }
        return;
    }
    if( event.Type == UIEVENT_MOUSEMOVE ) 
    {
        return;
    }
    if( event.Type == UIEVENT_BUTTONUP )
    {
        return;
    }
    if( event.Type == UIEVENT_MOUSEENTER )
    {
        if( IsEnabled() ) {
            m_uButtonState |= UISTATE_HOT;
            Invalidate();
        }
        return;
    }
    if( event.Type == UIEVENT_MOUSELEAVE )
    {
        if( (m_uButtonState & UISTATE_HOT) != 0 ) {
            m_uButtonState &= ~UISTATE_HOT;
            Invalidate();
        }
        return;
    }
    ZListElement::DoEvent(event);
}

SIZE ZListLabelElement::EstimateSize(SIZE szAvailable)
{
    if( m_pOwner == NULL ) return emptySize;
    return ZControl::EstimateSize(szAvailable);
}

void ZListLabelElement::DoPaint(ZRenderer* render, const RECT& rcPaint)
{
    if( !IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return;
    DrawItemBk(render, m_rcItem);
    DrawItemText(render, m_rcItem);
}

void ZListLabelElement::DrawItemText(ZRenderer* render, const RECT& rcItem)
{
    if( m_sText.IsEmpty() ) return;

    if( m_pOwner == NULL ) return;
    TListInfo* pInfo = m_pOwner->GetListInfo();
    color_t iTextColor = pInfo->dwTextColor;
    if( (m_uButtonState & UISTATE_HOT) != 0 ) {
        iTextColor = pInfo->dwHotTextColor;
    }
    if( IsSelected() ) {
        iTextColor = pInfo->dwSelectedTextColor;
    }
    if( !IsEnabled() ) {
        iTextColor = pInfo->dwDisabledTextColor;
    }
    int nLinks = 0;
    RECT rcText = rcItem;
    rcText.left += pInfo->rcTextPadding.left;
    rcText.right -= pInfo->rcTextPadding.right;
    rcText.top += pInfo->rcTextPadding.top;
    rcText.bottom -= pInfo->rcTextPadding.bottom;
    render->DrawText(pInfo->sFont, m_sText, pInfo->uFontSize, rcText, iTextColor, pInfo->uTextStyle);
}


/////////////////////////////////////////////////////////////////////////////////////
//
//

ZListContainerElement::ZListContainerElement() : 
m_iIndex(-1),
m_pOwner(NULL), 
m_bSelected(false),
m_uButtonState(0)
{
}

const char* ZListContainerElement::GetClass() const
{
    return "ListContainerElementUI";
}


void* ZListContainerElement::GetInterface(const char* pstrName)
{
    if( strcmp(pstrName, "ListItem") == 0 ) 
		return static_cast<IListItem*>(this);
	if( strcmp(pstrName, "ListContainerElement") == 0 ) 
		return static_cast<ZListContainerElement*>(this);
    return ZContainer::GetInterface(pstrName);
}

IListOwner* ZListContainerElement::GetOwner()
{
    return m_pOwner;
}

void ZListContainerElement::SetOwner(ZControl* pOwner)
{
    m_pOwner = static_cast<IListOwner*>(pOwner->GetInterface("IListOwner"));
}

void ZListContainerElement::SetVisible(bool bVisible)
{
    ZContainer::SetVisible(bVisible);
    if( !IsVisible() && m_bSelected)
    {
        m_bSelected = false;
        if( m_pOwner != NULL ) m_pOwner->SelectItem(-1);
    }
}

void ZListContainerElement::SetEnabled(bool bEnable)
{
    ZControl::SetEnabled(bEnable);
    if( !IsEnabled() ) {
        m_uButtonState = 0;
    }
}

int ZListContainerElement::GetIndex() const
{
    return m_iIndex;
}

void ZListContainerElement::SetIndex(int iIndex)
{
    m_iIndex = iIndex;
}

void ZListContainerElement::Invalidate()
{
    if( !IsVisible() ) return;

    if( GetParent() ) {
        ZContainer* pParentContainer = static_cast<ZContainer*>(GetParent()->GetInterface("Container"));
        if( pParentContainer ) {
            RECT rc = pParentContainer->GetPos();
            RECT rcInset = pParentContainer->GetInset();
            rc.left += rcInset.left;
            rc.top += rcInset.top;
            rc.right -= rcInset.right;
            rc.bottom -= rcInset.bottom;
            ZScrollBar* pVerticalScrollBar = pParentContainer->GetVerticalScrollBar();
            if( pVerticalScrollBar && pVerticalScrollBar->IsVisible() ) rc.right -= pVerticalScrollBar->GetFixedWidth();
            ZScrollBar* pHorizontalScrollBar = pParentContainer->GetHorizontalScrollBar();
            if( pHorizontalScrollBar && pHorizontalScrollBar->IsVisible() ) rc.bottom -= pHorizontalScrollBar->GetFixedHeight();

            RECT invalidateRc = m_rcItem;
            if( !IntersectRect(&invalidateRc, &m_rcItem, &rc) ) 
            {
                return;
            }

            ZControl* pParent = GetParent();
            RECT rcTemp;
            RECT rcParent;
            while( pParent = pParent->GetParent() )
            {
                rcTemp = invalidateRc;
                rcParent = pParent->GetPos();
                if( !IntersectRect(&invalidateRc, &rcTemp, &rcParent) ) 
                {
                    return;
                }
            }

            if( m_pManager != NULL ) m_pManager->Invalidate(invalidateRc);
        }
        else {
            ZContainer::Invalidate();
        }
    }
    else {
        ZContainer::Invalidate();
    }
}

bool ZListContainerElement::Activate()
{
    if( !ZContainer::Activate() ) return false;
    //if( m_pManager != NULL ) m_pManager->SendNotify(this, _T("itemactivate"));
    return true;
}

bool ZListContainerElement::IsSelected() const
{
    return m_bSelected;
}

bool ZListContainerElement::Select(bool bSelect)
{
    if( !IsEnabled() ) return false;
    if( bSelect == m_bSelected ) return true;
    m_bSelected = bSelect;
    if( bSelect && m_pOwner != NULL ) m_pOwner->SelectItem(m_iIndex);
    Invalidate();

    return true;
}

bool ZListContainerElement::IsExpanded() const
{
    return false;
}

bool ZListContainerElement::Expand(bool /*bExpand = true*/)
{
    return false;
}

void ZListContainerElement::DoEvent(TEventUI& event)
{
    if( !IsMouseEnabled() && event.Type > UIEVENT__MOUSEBEGIN && event.Type < UIEVENT__MOUSEEND ) {
        if( m_pOwner != NULL ) m_pOwner->DoEvent(event);
        else ZContainer::DoEvent(event);
        return;
    }

    if( event.Type == UIEVENT_DBLCLICK )
    {
        if( IsEnabled() ) {
            Activate();
            Invalidate();
        }
        return;
    }
    if( event.Type == UIEVENT_KEYDOWN && IsEnabled() )
    {
        if( event.chKey == SDLK_RETURN ) {
            Activate();
            Invalidate();
            return;
        }
    }
    if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_RBUTTONDOWN )
    {
        if( IsEnabled() ){
            //m_pManager->SendNotify(this, _T("itemclick"));
            Select();
            Invalidate();
        }
        return;
    }
    if( event.Type == UIEVENT_BUTTONUP ) 
    {
        return;
    }
    if( event.Type == UIEVENT_MOUSEMOVE )
    {
        return;
    }
    if( event.Type == UIEVENT_MOUSEENTER )
    {
        if( IsEnabled() ) {
            m_uButtonState |= UISTATE_HOT;
            Invalidate();
        }
        return;
    }
    if( event.Type == UIEVENT_MOUSELEAVE )
    {
        if( (m_uButtonState & UISTATE_HOT) != 0 ) {
            m_uButtonState &= ~UISTATE_HOT;
            Invalidate();
        }
        return;
    }

    // An important twist: The list-item will send the event not to its immediate
    // parent but to the "attached" list. A list may actually embed several components
    // in its path to the item, but key-presses etc. needs to go to the actual list.
    if( m_pOwner != NULL ) m_pOwner->DoEvent(event); else ZControl::DoEvent(event);
}

void ZListContainerElement::SetAttribute(const char* pstrName, const char* pstrValue)
{
    if( strcmp(pstrName, "selected") == 0 ) 
		Select();
    else 
		ZContainer::SetAttribute(pstrName, pstrValue);
}

void ZListContainerElement::DoPaint(ZRenderer* render, const RECT& rcPaint)
{
    if( !IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return;
    DrawItemBk(render, m_rcItem);
    ZContainer::DoPaint(render, rcPaint);
}

void ZListContainerElement::DrawItemText(ZRenderer* render, const RECT& rcItem)
{
    return;
}

void ZListContainerElement::DrawItemBk(ZRenderer* render, const RECT& rcItem)
{
    assert(m_pOwner);
    if( m_pOwner == NULL ) return;
    TListInfo* pInfo = m_pOwner->GetListInfo();
    color_t iBackColor = 0;
    if( !pInfo->bAlternateBk || m_iIndex % 2 == 0 ) 
		iBackColor = pInfo->dwBkColor;

    if( (m_uButtonState & UISTATE_HOT) != 0 ) {
        iBackColor = pInfo->dwHotBkColor;
    }

    if( IsSelected() ) {
        iBackColor = pInfo->dwSelectedBkColor;
    }

    if( !IsEnabled() ) {
        iBackColor = pInfo->dwDisabledBkColor;
    }

    if ( iBackColor != 0 ) {
		render->SetDrawColor(iBackColor);
		render->FillRect(m_rcItem);
    }

    if( !IsEnabled() ) {
        if( !pInfo->sDisabledImage.IsEmpty() ) {
            if( !DrawImage(render,&pInfo->sDisabledImage) ) 
                pInfo->sDisabledImage.Clear();
            else 
                return;
        }
    }
    if( IsSelected() ) {
        if( !pInfo->sSelectedImage.IsEmpty() ) {
            if( !DrawImage(render, &pInfo->sSelectedImage) )
                pInfo->sSelectedImage.Clear();
            else 
                return;
        }
    }
    if( (m_uButtonState & UISTATE_HOT) != 0 ) {
        if( !pInfo->sHotImage.IsEmpty() ) {
            if( !DrawImage(render, &pInfo->sHotImage) ) 
                pInfo->sHotImage.Clear();
            else 
                return;
        }
    }
    if( !m_sBkImage.IsEmpty() ) {
        if( !pInfo->bAlternateBk || m_iIndex % 2 == 0 ) {
            DrawImage(render, &m_sBkImage);
        }
    }

    if( m_sBkImage.IsEmpty() ) {
        if( !pInfo->sBkImage.IsEmpty() ) {
            if( !DrawImage(render, &pInfo->sBkImage) ) 
                pInfo->sBkImage.Clear();
            else 
                return;
        }
    }

    if ( pInfo->dwLineColor != 0 ) {
        RECT rcLine = { m_rcItem.left, m_rcItem.bottom - 1, m_rcItem.right, m_rcItem.bottom - 1 };
		render->SetDrawColor(pInfo->dwLineColor);
		render->FillRect(rcLine);
    }
}

} // namespace DuiLib
